package tinyGA;

import tinyGA.TinyVM;

abstract public class Instruccion {

	public enum ICOD {
		SUM, RESTA, MUL, DIV, AND, OR, EQ, NEQ, GT, GE, LT, LE, IR_F, IR_A, APILA, DESAPILA_DIR, APILA_DIR, APILA_TRUE, APILA_FALSE, NOT, MENOS
	};

	abstract public ICOD ci();
	abstract public void ejecuta(TinyVM vm);

	public int arg1() {
		throw new UnsupportedOperationException("arg1");
	}

	public static class ISuma extends Instruccion {
		ISuma() {
		}

		public ICOD ci() {
			return ICOD.SUM;
		}

		public String toString() {
			return "SUMA";
		}
		
		public void ejecuta(TinyVM vm) {
	         TinyVM.PValue op2 = vm.pop();
	         TinyVM.PValue op1 = vm.pop();
	         vm.push(new TinyVM.IntPValue(op1.asInt()+op2.asInt()));
	         vm.incCP();
	       }
	}

	public static class IResta extends Instruccion {
		IResta() {
		}

		public ICOD ci() {
			return ICOD.RESTA;
		}

		public String toString() {
			return "RESTA";
		}
		
		public void ejecuta(TinyVM vm) {
	         TinyVM.PValue op2 = vm.pop();
	         TinyVM.PValue op1 = vm.pop();
	         vm.push(new TinyVM.IntPValue(op1.asInt()-op2.asInt()));
	         vm.incCP();
	       }
	}

	public static class IMul extends Instruccion {
		IMul() {
		}

		public ICOD ci() {
			return ICOD.MUL;
		}

		public String toString() {
			return "MUL";
		}
		
		public void ejecuta(TinyVM vm) {
	         TinyVM.PValue op2 = vm.pop();
	         TinyVM.PValue op1 = vm.pop();
	         vm.push(new TinyVM.IntPValue(op1.asInt()*op2.asInt()));
	         vm.incCP();
	       }
	}

	public static class IDiv extends Instruccion {
		IDiv() {
		}

		public ICOD ci() {
			return ICOD.DIV;
		}

		public String toString() {
			return "DIV";
		}
		
		public void ejecuta(TinyVM vm) {
	         TinyVM.PValue op2 = vm.pop();
	         TinyVM.PValue op1 = vm.pop();
	         vm.push(new TinyVM.IntPValue(op1.asInt()/op2.asInt()));
	         vm.incCP();
	       }
	}

	public static class IOr extends Instruccion {
		IOr() {
		}

		public ICOD ci() {
			return ICOD.OR;
		}

		public String toString() {
			return "OR";
		}
		
		public void ejecuta(TinyVM vm) {
	         TinyVM.PValue op2 = vm.pop();
	         TinyVM.PValue op1 = vm.pop();
	         vm.push(new TinyVM.BoolPValue(op1.asBool()||op2.asBool()));
	         vm.incCP();
	       }
	}

	public static class IAnd extends Instruccion {
		IAnd() {
		}

		public ICOD ci() {
			return ICOD.AND;
		}

		public String toString() {
			return "AND";
		}
		
		public void ejecuta(TinyVM vm) {
	         TinyVM.PValue op2 = vm.pop();
	         TinyVM.PValue op1 = vm.pop();
	         vm.push(new TinyVM.BoolPValue(op1.asBool() && op2.asBool()));
	         vm.incCP();
	       }
	}

	public static class IEq extends Instruccion {
		IEq() {
		}

		public ICOD ci() {
			return ICOD.EQ;
		}

		public String toString() {
			return "EQ";
		}
		
		public void ejecuta(TinyVM vm) {
	         TinyVM.PValue op2 = vm.pop();
	         TinyVM.PValue op1 = vm.pop();
	         vm.push(new TinyVM.BoolPValue(op1.asInt()==op2.asInt()));
	         vm.incCP();
	       }
	}

	public static class INeq extends Instruccion {
		INeq() {
		}

		public ICOD ci() {
			return ICOD.NEQ;
		}

		public String toString() {
			return "NEQ";
		}
		
		public void ejecuta(TinyVM vm) {
	         TinyVM.PValue op2 = vm.pop();
	         TinyVM.PValue op1 = vm.pop();
	         vm.push(new TinyVM.BoolPValue(op1.asInt()!=op2.asInt()));
	         vm.incCP();
	       }
	}

	public static class IGt extends Instruccion {
		IGt() {
		}

		public ICOD ci() {
			return ICOD.GT;
		}

		public String toString() {
			return "GT";
		}
		
		public void ejecuta(TinyVM vm) {
	         TinyVM.PValue op2 = vm.pop();
	         TinyVM.PValue op1 = vm.pop();
	         vm.push(new TinyVM.BoolPValue(op1.asInt()>op2.asInt()));
	         vm.incCP();
	       }
	}

	public static class IGe extends Instruccion {
		IGe() {
		}

		public ICOD ci() {
			return ICOD.GE;
		}

		public String toString() {
			return "GE";
		}
		
		public void ejecuta(TinyVM vm) {
	         TinyVM.PValue op2 = vm.pop();
	         TinyVM.PValue op1 = vm.pop();
	         vm.push(new TinyVM.BoolPValue(op1.asInt()>=op2.asInt()));
	         vm.incCP();
	       }
	}

	public static class ILt extends Instruccion {
		ILt() {
		}

		public ICOD ci() {
			return ICOD.LT;
		}

		public String toString() {
			return "LT";
		}
		
		public void ejecuta(TinyVM vm) {
	         TinyVM.PValue op2 = vm.pop();
	         TinyVM.PValue op1 = vm.pop();
	         vm.push(new TinyVM.BoolPValue(op1.asInt()<op2.asInt()));
	         vm.incCP();
	       }
	}

	public static class ILe extends Instruccion {
		ILe() {
		}

		public ICOD ci() {
			return ICOD.LE;
		}

		public String toString() {
			return "LE";
		}
		
		public void ejecuta(TinyVM vm) {
	         TinyVM.PValue op2 = vm.pop();
	         TinyVM.PValue op1 = vm.pop();
	         vm.push(new TinyVM.BoolPValue(op1.asInt()<=op2.asInt()));
	         vm.incCP();
	       }
	}

	public static class Ir_f extends Instruccion {
		Ir_f(String val) {
			try {
				this.val = Integer.valueOf(val).intValue();
			} catch (NumberFormatException e) {
				this.val = 0;
			}
		}

		Ir_f(int val) {
			this.val = val;
		}

		public ICOD ci() {
			return ICOD.IR_F;
		}

		public int arg1() {
			return val;
		}

		public String toString() {
			return "IR_F(" + val + ")";
		}
		
		//TODO
		public void ejecuta(TinyVM vm) {
	         TinyVM.PValue op1 = vm.pop();
	         if(op1.asBool()){
	        	 vm.setCP(val);//o ponemos val-1??
	         }
	         else{
	        	 vm.incCP(); 
	         }
	         //vm.push(new TinyVM.BoolPValue(op1.asInt()<=op2.asInt()));
	         //vm.incCP();
	    }

		private int val;

	}

	public static class Ir_a extends Instruccion {
		Ir_a(String val) {
			try {
				this.val = Integer.valueOf(val).intValue();
			} catch (NumberFormatException e) {
				this.val = 0;
			}
		}

		Ir_a(int val) {
			this.val = val;
		}

		public ICOD ci() {
			return ICOD.IR_A;
		}

		public int arg1() {
			return val;
		}

		public String toString() {
			return "IR_A(" + val + ")";
		}
		
		public void ejecuta(TinyVM vm) {
			vm.setCP(val);//o ponemos val-1??
	    }

		private int val;

	}

	public static class IApila extends Instruccion {
		IApila(String val) {
			try {
				this.val = Integer.valueOf(val).intValue();
			} catch (NumberFormatException e) {
				this.val = 0;
			}
		}

		public ICOD ci() {
			return ICOD.APILA;
		}

		public int arg1() {
			return val;
		}

		public String toString() {
			return "APILA(" + val + ")";
		}

		public void ejecuta(TinyVM vm) {
	         vm.push(new TinyVM.IntPValue(val));
	         vm.incCP();
	       }
		
		private int val;
	}

	public static class IDesapila_dir extends Instruccion {
		IDesapila_dir(String val) {
			try {
				this.val = Integer.valueOf(val).intValue();
			} catch (NumberFormatException e) {
				this.val = 0;
			}
		}

		public ICOD ci() {
			return ICOD.DESAPILA_DIR;
		}

		public int arg1() {
			return val;
		}

		public String toString() {
			return "DESAPILA_DIR(" + val + ")";
		}
		
		public void ejecuta(TinyVM vm) {
	         vm.pop();
	         vm.incCP();
	       }

		private int val;
	}

	public static class IApila_dir extends Instruccion {
		IApila_dir(String val) {
			try {
				this.val = Integer.valueOf(val).intValue();
			} catch (NumberFormatException e) {
				this.val = 0;
			}
		}

		public ICOD ci() {
			return ICOD.APILA_DIR;
		}

		public int arg1() {
			return val;
		}

		public String toString() {
			return "APILA_DIR(" + val + ")";
		}

		public void ejecuta(TinyVM vm) {
	         vm.push(new TinyVM.IntPValue(val));
	         vm.incCP();
	    }
		
		private int val;
	}

	public static class IApila_true extends Instruccion {
		IApila_true() {
		}

		public ICOD ci() {
			return ICOD.APILA_TRUE;
		}

		public String toString() {
			return "APILA_TRUE";
		}
		
		public void ejecuta(TinyVM vm) {
	         vm.push(new TinyVM.BoolPValue(true));
	         vm.incCP();
	    }
	}

	public static class IApila_false extends Instruccion {
		IApila_false() {
		}

		public ICOD ci() {
			return ICOD.APILA_FALSE;
		}

		public String toString() {
			return "APILA_FALSE";
		}
		
		public void ejecuta(TinyVM vm) {
	         vm.push(new TinyVM.BoolPValue(false));
	         vm.incCP();
	    }
	}

	public static class INot extends Instruccion {
		INot() {
		}

		public ICOD ci() {
			return ICOD.NOT;
		}

		public String toString() {
			return "NOT";
		}
		
		public void ejecuta(TinyVM vm) {
	         TinyVM.PValue op1 = vm.pop();
	         vm.push(new TinyVM.BoolPValue(!op1.asBool()));
	         vm.incCP();
	       }
	}

	public static class IMenos extends Instruccion {
		IMenos() {
		}

		public ICOD ci() {
			return ICOD.MENOS;
		}

		public String toString() {
			return "MENOS";
		}
		
		public void ejecuta(TinyVM vm) {
	         TinyVM.PValue op1 = vm.pop();
	         vm.push(new TinyVM.IntPValue(-op1.asInt()));
	         vm.incCP();
	       }
	}

	public static Instruccion nuevaISuma() {
		if (isuma == null) {
			isuma = new ISuma();
		}
		return isuma;
	}

	public static Instruccion nuevaIResta() {
		if (iresta == null) {
			iresta = new IResta();
		}
		return iresta;
	}

	public static Instruccion nuevaIMul() {
		if (imul == null) {
			imul = new IMul();
		}
		return imul;
	}

	public static Instruccion nuevaIDiv() {
		if (idiv == null) {
			idiv = new IDiv();
		}
		return idiv;
	}

	public static Instruccion nuevaIAnd() {
		if (iand == null) {
			iand = new IAnd();
		}
		return iand;
	}

	public static Instruccion nuevaIOr() {
		if (ior == null) {
			ior = new IOr();
		}
		return ior;
	}

	public static Instruccion nuevaIEq() {
		if (ieq == null) {
			ieq = new IEq();
		}
		return ieq;
	}

	public static Instruccion nuevaINeq() {
		if (ineq == null) {
			ineq = new INeq();
		}
		return ineq;
	}

	public static Instruccion nuevaIGt() {
		if (igt == null) {
			igt = new IGt();
		}
		return igt;
	}

	public static Instruccion nuevaIGe() {
		if (ige == null) {
			ige = new IGe();
		}
		return ige;
	}

	public static Instruccion nuevaILt() {
		if (ilt == null) {
			ilt = new ILt();
		}
		return ilt;
	}

	public static Instruccion nuevaILe() {
		if (ile == null) {
			ile = new ILe();
		}
		return ile;
	}

	public static Instruccion nuevaIr_f(String val) {
		return new Ir_f(val);
	}

	public static Instruccion nuevaIr_a(String val) {
		return new Ir_a(val);
	}

	public static Instruccion nuevaIApila(String val) {
		return new IApila(val);
	}

	public static Instruccion nuevaIDesapila_dir(String val) {
		return new IDesapila_dir(val);
	}

	public static Instruccion nuevaIApila_true() {
		if (ipatrue == null) {
			ipatrue = new IApila_true();
		}
		return ipatrue;
	}

	public static Instruccion nuevaIApila_false() {
		if (ipafalse == null) {
			ipafalse = new IApila_false();
		}
		return ipafalse;
	}

	public static Instruccion nuevaINot() {
		if (inot == null) {
			inot = new INot();
		}
		return inot;
	}

	public static Instruccion nuevaIMenos() {
		if (imenos == null) {
			imenos = new IMenos();
		}
		return imenos;
	}

	public static Instruccion nuevaIApila_dir(String val) {
		return new IApila_dir(val);
	}

	private static ISuma isuma = null;
	private static IResta iresta = null;
	private static IMul imul = null;
	private static IDiv idiv = null;
	private static IAnd iand = null;
	private static IOr ior = null;
	private static IEq ieq = null;
	private static INeq ineq = null;
	private static IGt igt = null;
	private static IGe ige = null;
	private static ILt ilt = null;
	private static ILe ile = null;
	private static IApila_true ipatrue = null;
	private static IApila_false ipafalse = null;
	private static IMenos imenos = null;
	private static INot inot = null;

}
